package cc.chenwenxi.zb.rest.service;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ser.std.DateSerializer;
import com.google.common.collect.Lists;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.okcoin.commons.okex.open.api.bean.spot.result.Product;

import cc.chenwenxi.papaya.entity.exchange.Symbol;
import cc.chenwenxi.papaya.entity.exchange.Symbol.Quote;
import cc.chenwenxi.zb.rest.domain.config.ZbApiConfig;
import cc.chenwenxi.zb.rest.domain.resp.TickerBody.Ticker;
import cc.chenwenxi.zb.rest.exception.ApiException;
import cc.chenwenxi.zb.rest.security.AuthenticationInterceptor;
import cn.hutool.core.util.NumberUtil;
import jodd.util.Wildcard;
import lombok.extern.slf4j.Slf4j;
import melody.base.entity.res.KlineResult;
import net.sf.json.JSONArray;
import okhttp3.OkHttpClient;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Response;
import retrofit2.Retrofit;

@Slf4j
public class ApiClient {

	private Retrofit.Builder builder;
	private Retrofit retrofit;
	private ZbApiConfig config;

	public ApiClient(ZbApiConfig config, String baseUrl) {
		super();
		this.config = config;
		builder = new Retrofit.Builder().baseUrl(baseUrl);
//		.addConverterFactory(GsonConverterFactory.create());//JacksonConverterFactory.create() // FastJsonConverterFactory.create()
		retrofit = builder.build();
	}

	public <S> S createService(Class<S> serviceClass) {
		OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
		AuthenticationInterceptor interceptor = new AuthenticationInterceptor(config);// 统一过滤器
		if (!httpClient.interceptors().contains(interceptor)) {
			httpClient.addInterceptor(interceptor);
			builder.client(httpClient.build());
			retrofit = builder.build();
		}
		return retrofit.create(serviceClass);
	}// 呵呵..zb的json返回不统一,只能手动处理不同类型,错误只返回{"error":"市场错误"}

	public static <T> T executeSync(Call<ResponseBody> call, Class<T> t) {
		try {
			Response<ResponseBody> response = call.execute();
//			log.info(response.toString());
			if (response.isSuccessful()) {
				String jsonRes = response.body().string();
//				log.info("返回11json:" + jsonRes);
				parseBody(jsonRes);// 处理异常
				List<String> pathSegments = response.raw().request().url().pathSegments();
				if (pathSegments.contains("kline")) {
					return (T) getKline(jsonRes);
				}
				else if (pathSegments.contains("getUnfinishedOrdersIgnoreTradeType")) {
					// 需要返回list 还要判断 {"code":3001,"message":"挂单没有找到或已完成"}
					try {
						return (T) JSONObject.parseArray(jsonRes);
					} catch (JSONException e) {
						return (T) Lists.newArrayList();
					}
				}
				else if (pathSegments.contains("allTicker")) {
					return (T) getTickers(jsonRes);
				} else if (pathSegments.contains("markets")) {
					return (T) getMarkets(jsonRes);
				}

				T fromJson = new Gson().fromJson(jsonRes, t);
				return fromJson;
			}
		} catch (IOException | ApiException e) {
			log.error(e.getMessage(), e);
		}
		throw new IllegalStateException("invalid response from server.");
	}

	private static List<Product> getMarkets(String json) {
		JSONObject parseObject = JSONObject.parseObject(json);
		Set<String> keySet = parseObject.keySet();
		return keySet.stream().map(key -> {
			JSONObject jsonObject = parseObject.getJSONObject(key);
			String[] split = key.split("_");
			Symbol symbol = new Symbol(split[0].toLowerCase(), Quote.getEnum(split[1]));
			return new Product().setInstrument_id(key).setBase_currency(symbol.getCoin())
					.setQuote_currency(symbol.getQuote())
					.setMin_size(calculateDecimalPoint(jsonObject.getInteger("amountScale")))
					.setTick_size(calculateDecimalPoint(jsonObject.getInteger("priceScale")));
		}).collect(Collectors.toList());
	}

	// 计算小数点
	private static BigDecimal calculateDecimalPoint(int scale) {
		String num = "0.";
		for (int i = 0; i < scale - 1; i++) {
			num += "0";
		}
		num += 1;
		return NumberUtil.toBigDecimal(num);
	}

	private static List<Ticker> getTickers(String json) {
		com.alibaba.fastjson.JSONObject parseObject = com.alibaba.fastjson.JSONObject.parseObject(json);
		return parseObject.keySet().stream().map(instrument_id -> {
			Ticker ticker = parseObject.getObject(instrument_id, Ticker.class);
			ticker.setInstrument_id(instrument_id);
			return ticker;
		}).collect(Collectors.toList());
	}

	private static List<KlineResult> getKline(String json) {
		com.alibaba.fastjson.JSONObject klineObj = com.alibaba.fastjson.JSONObject.parseObject(json);
		json = klineObj.getString("data");
		return Lists.newArrayList(JSONArray.fromObject(json).toArray()).stream().map(o -> {
			JSONArray k = (JSONArray) o;
			KlineResult kl = new KlineResult();
			int i = 0;
			kl.setDate(Long.parseLong(k.get(i++).toString()));
			kl.setOpen(new BigDecimal(k.get(i++).toString()));
			kl.setHigh(new BigDecimal(k.get(i++).toString()));
			kl.setLow(new BigDecimal(k.get(i++).toString()));
			kl.setClose(new BigDecimal(k.get(i++).toString()));
			kl.setVol(new BigDecimal(k.get(i++).toString()));
			return kl;
		}).collect(Collectors.toList());
	}

	// zb专属的异常处理方式
	private static <T> void parseBody(String json) throws ApiException {
		// 避过array获取不到error的坑,先检测字符串是否有error关键字
		if (Wildcard.match(json, "*error*")) {
			Optional.ofNullable(JSONObject.parseObject(json).getOrDefault("error", null)).ifPresent(error -> {
				throw new ApiException(error.toString());
			});
		}
	}
}
